package org.yunai.swjg.server.module.scene.core;

import org.slf4j.Logger;
import org.yunai.swjg.server.core.annotation.MainThread;
import org.yunai.swjg.server.core.message.GameMessage;
import org.yunai.swjg.server.core.service.Online;
import org.yunai.swjg.server.core.service.OnlineContextService;
import org.yunai.yfserver.common.HeartBeatable;
import org.yunai.yfserver.common.LoggerFactory;
import org.yunai.yfserver.common.Tickable;
import org.yunai.yfserver.spring.BeanManager;
import org.yunai.yfserver.util.ObjectUtils;

import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * 场景内玩家管理器
 * User: yunai
 * Date: 13-4-22
 * Time: 下午4:14
 */
public class SceneOnlineManager implements Tickable, HeartBeatable {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.scene, SceneOnlineManager.class);

    /**
     * 在线玩家容器服务
     */
    private static OnlineContextService onlineContextService;
    static {
        onlineContextService = BeanManager.getBean(OnlineContextService.class);
    }

    /**
     * 场景
     */
    private final AbstractScene scene;
    /**
     * 玩家ID的列表<br />
     * 此处不引用玩家的实例，获取玩家实力通过onlineContextService
     */
    private volatile Set<Integer> playerIds;

    public SceneOnlineManager(AbstractScene scene) {
        this.scene = scene;
        this.playerIds = new CopyOnWriteArraySet<>();
    }

    /**
     * [主线程]添加场景中玩家
     *
     * @param playerId 玩家编号
     */
    @MainThread
    public void add(Integer playerId) {
        boolean added = playerIds.add(playerId);
        if (!added) {
            LOGGER.error("[add] [scene({}) added player({})].", scene.getSceneId(), playerId);
        }
    }

    /**
     * [主线程]移除场景中玩家
     *
     * @param playerId 玩家编号
     */
    @MainThread
    public void remove(Integer playerId) {
        if (playerId == null) {
            return;
        }
        boolean removed = playerIds.remove(playerId);
        if (!removed) {
            LOGGER.error("[remove] [scene({}) removed player({})].", scene.getSceneId(), playerId);
        }
    }

    /**
     * @param playerId 玩家编号
     * @return 该玩家是否在场景中
     */
    private boolean containPlayer(Integer playerId) {
        return playerIds.contains(playerId);
    }

    /**
     * @param playerId 玩家编号
     * @return 玩家的在线信息
     */
    public Online getOnline(Integer playerId) {
        return containPlayer(playerId) ? onlineContextService.getPlayer(playerId) : null;
    }

    /**
     * @return 场景中玩家集合，该集合无法进行修改
     */
    public Set<Integer> getPlayerIds() {
        return Collections.unmodifiableSet(playerIds);
    }

    /**
     * @return 场景玩家数量
     */
    public int size() {
        return playerIds.size();
    }

    /**
     * 处理场景内玩家的输入输出消息
     */
    @Override
    public void tick() {
        for (Integer playerId : playerIds) {
            Online online = onlineContextService.getPlayer(playerId);
            if (online == null || !ObjectUtils.isSame(online.getScene(), scene)) {
                continue;
            }
System.err.println("场景(" + scene.getSceneId() + "):玩家(" + playerId + ")在处理消息.");
            online.processMessage();
        }
    }

    // TODO tick / heartBeat 遍历方式不同，想想看看哪个是OK的。。。

    @Override
    public void heartBeat() {
        for (Iterator<Integer> iterator = playerIds.iterator(); iterator.hasNext();) {
            Integer playerId = iterator.next();
            if (playerId == null) {
                continue;
            }
            Online online = onlineContextService.getPlayer(playerId);
            if (online == null || !ObjectUtils.isSame(online.getScene(), scene)) {
                continue;
            }
System.err.println("场景(" + scene.getSceneId() + "):玩家(" + playerId + ")在心跳.");
            online.heartBeat();
        }
    }

    public void sendGameMessage(GameMessage gameMessage, Set<Integer> exPlayerIdSet) {
        for (Iterator<Integer> iterator = playerIds.iterator(); iterator.hasNext();) {
            Integer playerId = iterator.next();
            if (playerId == null || exPlayerIdSet.contains(playerId)) {
                continue;
            }
            Online online = onlineContextService.getPlayer(playerId);
            if (online != null && ObjectUtils.isSame(online.getScene(), scene)) {
                online.write(gameMessage);
            }
        }
    }
}
